package com.admin.system.modules.system.service.impl;

import com.admin.common.exception.BadRequestException;
import com.admin.common.exception.EntityExistException;
import com.admin.common.utils.StringUtils;
import com.admin.core.dto.MenuQueryDTO;
import com.admin.core.entity.SysMenu;
import com.admin.core.entity.SysRole;
import com.admin.core.entity.SysRoleMenu;
import com.admin.core.mapper.MenuMapper;
import com.admin.core.mapper.RoleMenuMapper;
import com.admin.oauth.util.LoginUserUtil;
import com.admin.system.modules.system.service.MenuService;
import com.admin.system.modules.system.service.RoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author sq-lan
 */
@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, SysMenu> implements MenuService {

    private final MenuMapper menuMapper;
    private final RoleMenuMapper roleMenuMapper;
    private final RoleService roleService;

    private static final String HTTP_PRE = "http://";
    private static final String HTTPS_PRE = "https://";
    private static final String BAD_REQUEST = "外链必须以http://或者https://开头";

    @Override
    public List<SysMenu> list(MenuQueryDTO dto) {
        LambdaQueryWrapper<SysMenu> wrapper = listMyWrapper();
        if (org.springframework.util.StringUtils.hasText(dto.getBlurry())) {
            wrapper.and(w -> w.like(SysMenu::getTitle, dto.getBlurry())
                    .or().like(SysMenu::getComponentName, dto.getBlurry())
                    .or().like(SysMenu::getPermission, dto.getBlurry()));
        }
        if (dto.getCreateTime() != null && !dto.getCreateTime().isEmpty()) {
            wrapper.between(SysMenu::getCreateTime, dto.getCreateTime().get(0), dto.getCreateTime().get(1));
        }
        if (dto.getPid() != null && !dto.getPid().equals(0)) {
            wrapper.eq(SysMenu::getPid, dto.getPid());
        } else {
            wrapper.isNull(SysMenu::getPid);
        }
        return list(wrapper);
    }

    @Override
    public List<SysMenu> listMyAll() {
        LambdaQueryWrapper<SysMenu> wrapper = listMyWrapper();
        return list(wrapper);
    }

    /**
     * @param type /
     * @return /
     */
    @Override
    public List<SysMenu> listMyFilterType(Integer type) {
        LambdaQueryWrapper<SysMenu> wrapper = listMyWrapper();
        return menuMapper.selectList(wrapper.ne(type != null, SysMenu::getType, type));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(SysMenu entity) {
        if (this.count(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getTitle, entity.getTitle())) > 0) {
            throw new EntityExistException("菜单名称", entity.getTitle());
        }
        if (StringUtils.isNotBlank(entity.getComponentName()) && this.count(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getComponentName, entity.getComponentName())) > 0) {
            throw new EntityExistException("菜单组件", entity.getComponentName());
        }
        if (0 == entity.getPid()) {
            entity.setPid(null);
        }
        if (entity.getIFrame()) {
            if (!(entity.getPath().toLowerCase().startsWith(HTTP_PRE) || entity.getPath().toLowerCase().startsWith(HTTPS_PRE))) {
                throw new BadRequestException(BAD_REQUEST);
            }
        }
        save(entity);
        // 更新父节点菜单数目
        if (entity.getPid() != null && 0 != entity.getPid()) {
            updateSubCount(entity.getPid());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void modify(SysMenu entity) {
        if (entity.getId().equals(entity.getPid())) {
            throw new BadRequestException("上级不能为自己");
        }
        if (this.count(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getTitle, entity.getTitle()).ne(SysMenu::getId, entity.getId())) > 0) {
            throw new EntityExistException("菜单名称", entity.getTitle());
        }
        if (StringUtils.isNotBlank(entity.getComponentName())) {
            if (this.count(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getComponentName, entity.getComponentName()).ne(SysMenu::getId, entity.getId())) > 0) {
                throw new EntityExistException("菜单组件", entity.getComponentName());
            }
        }
        if (entity.getIFrame()) {
            if (!(entity.getPath().toLowerCase().startsWith(HTTP_PRE) || entity.getPath().toLowerCase().startsWith(HTTPS_PRE))) {
                throw new BadRequestException(BAD_REQUEST);
            }
        }

        SysMenu menu = getById(entity.getId());
        if (entity.getPid().equals(0)) {
            entity.setPid(null);
        }
        Integer newPid = entity.getPid();
        Integer oldPid = menu.getPid();

        menu.setTitle(entity.getTitle());
        menu.setComponent(entity.getComponent());
        menu.setPath(entity.getPath());
        menu.setIcon(entity.getIcon());
        menu.setIFrame(entity.getIFrame());
        menu.setPid(entity.getPid());
        menu.setMenuSort(entity.getMenuSort());
        menu.setCache(entity.getCache());
        menu.setHidden(entity.getHidden());
        menu.setComponentName(entity.getComponentName());
        menu.setPermission(entity.getPermission());
        menu.setType(entity.getType());
        this.updateById(menu);
        // 计算父级菜单节点数目
        updateSubCount(oldPid);
        updateSubCount(newPid);
    }

    @Override
    public Set<SysMenu> listChildAllAndSelf(List<SysMenu> menuList, Set<SysMenu> menuSet) {
        for (SysMenu menu : menuList) {
            menuSet.add(menu);
            List<SysMenu> menus = this.listByPid(menu.getId());
            if (menus != null && menus.size() != 0) {
                listChildAllAndSelf(menus, menuSet);
            }
        }
        return menuSet;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(Set<SysMenu> menuSet) {
        for (SysMenu menu : menuSet) {
            roleMenuMapper.delete(Wrappers.<SysRoleMenu>lambdaQuery().eq(SysRoleMenu::getMenuId, menu.getId()));
            menuMapper.deleteById(menu.getId());
            updateSubCount(menu.getPid());
        }
    }

    @Override
    public List<SysMenu> listSuperior(SysMenu menu, List<SysMenu> menus) {
        if (menu.getPid() == null) {
            menus.addAll(listByPid(null));
            return menus;
        }
        menus.addAll(this.listByPid(menu.getPid()));
        return listSuperior(getById(menu.getPid()), menus);
    }

    @Override
    public List<SysMenu> listByPid(Integer pid) {
        LambdaQueryWrapper<SysMenu> wrapper = listMyWrapper();
        wrapper.orderByAsc(SysMenu::getMenuSort);
        if (pid != null && !pid.equals(0)) {
            wrapper.eq(SysMenu::getPid, pid);
        } else {
            wrapper.isNull(SysMenu::getPid);
        }
        return this.list(wrapper);
    }

    private LambdaQueryWrapper<SysMenu> listMyWrapper() {
        if (LoginUserUtil.user().getIsAdmin()) {
            return Wrappers.lambdaQuery();
        }
        List<SysRole> roles = roleService.listByUsersId(LoginUserUtil.userId());
        Set<Integer> roleIds = roles.stream().map(SysRole::getId).collect(Collectors.toSet());
        List<SysRoleMenu> roleMenus = roleMenuMapper.selectList(Wrappers.<SysRoleMenu>lambdaQuery().in(SysRoleMenu::getRoleId, roleIds));
        List<Integer> menuIds = roleMenus.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
        return Wrappers.<SysMenu>lambdaQuery().in(SysMenu::getId, menuIds);
    }

    private void updateSubCount(Integer menuId) {
        if (menuId != null && !menuId.equals(0)) {
            int count = this.count(Wrappers.<SysMenu>lambdaQuery().eq(SysMenu::getPid, menuId));
            menuMapper.updateSubCntById(count, menuId);
        }
    }

}
